package com.zlcx.tz.live.common.interceptor;

import com.zlcx.tz.live.common.annotation.RepeatSubmit;
import com.zlcx.tz.live.common.wrapper.RequestBodyCachingWrapper;
import com.zlcx.tz.live.constant.RedisKeyConst;
import com.zlcx.tz.live.exception.RequestLimitException;
import com.zlcx.tz.live.utils.MD5Util;
import com.zlcx.tz.live.utils.SignUtil;
import com.zlcx.tz.live.utils.SimpleLockUtil;
import io.swagger.models.HttpMethod;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * <p></p>
 * <PRE>
 * <BR>    修改记录
 * <BR>-----------------------------------------------
 * <BR>    修改日期         修改人          修改内容
 * </PRE>
 *
 * @author zl
 * @version 1.0
 * @date Created in 2019/5/15 19:34
 * @copyright: Copyright (c) founders
 */
@Component
@Slf4j
public class RepeatSubmitInterceptor extends HandlerInterceptorAdapter {


    @Autowired
    SimpleLockUtil simpleLockUtil;

    private static final String IGNORE_CONTENT_TYPE = "multipart";


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (HttpMethod.OPTIONS.toString().equalsIgnoreCase(request.getMethod())) {
            return true;
        }

        if (!(request instanceof RequestBodyCachingWrapper)) {
            request = new RequestBodyCachingWrapper(request);
        }
        if (!(response instanceof ContentCachingResponseWrapper)) {
            response = new ContentCachingResponseWrapper(response);
        }

        RepeatSubmit annotation;
        if (handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(RepeatSubmit.class);
        } else {
            return true;
        }

        if (annotation == null) {
            return true;
        }
        String paramsStr = SignUtil.buildMapToSign(request.getParameterMap());
        String requestBody = "";
        if (!request.getRequestURI().startsWith(IGNORE_CONTENT_TYPE)) {
            requestBody = getRequestBody(request);
        }

        String submitLockKey = RedisKeyConst.getSubmitLockKey(getSubmitLockKey(request, paramsStr, requestBody));
        if (simpleLockUtil.acquireLock(submitLockKey, submitLockKey, 1000)) {
            request.setAttribute("submitLockKey", submitLockKey);
            return true;
        } else {
            log.warn("[RepeatSubmitInterceptor]操作太频繁,IP={},RequestURI={},Method={},Parameter={},RequestBody={}", getIpAddr(request), request.getRequestURI(),
                    request.getMethod(), paramsStr, requestBody);
            throw new RequestLimitException("操作太频繁！");
        }

    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        String lockval = (String) request.getAttribute("submitLockKey");
        if (lockval != null) {
            simpleLockUtil.releaseLock(lockval, lockval);
        }
    }


    private String getSubmitLockKey(HttpServletRequest request, String paramsStr, String requestBody) {
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(getIpAddr(request))
                .append(request.getRequestURI())
                .append(request.getMethod())
                .append(paramsStr)
                .append(requestBody);
        return MD5Util.MD5Normal(keyBuilder.toString());
    }

    private String getRequestBody(HttpServletRequest request) {
        String requestBody = "";
        try {
            RequestBodyCachingWrapper wrapper = new RequestBodyCachingWrapper(request);
            requestBody = IOUtils.toString(wrapper.getInputStream(), wrapper.getCharacterEncoding());
        } catch (IOException e) {
        }
        return requestBody;
    }


    public String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("x-forwarded-for");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader(" Proxy-Client-IP ");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader(" WL-Proxy-Client-IP ");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }


}
